﻿using System;
using System.Runtime.Serialization;
using CI.Interfaces;
using CI.Organism;
using CI.Utils;

namespace CI
{
    /// <summary>
    /// singleton class to hold the physical hosting environment
    /// </summary>
    [DataContract]
    [Serializable]
    public sealed class SimEnv
    {
        /// <summary>singleton instance</summary>
        private static volatile SimEnv _instance;
        /// <summary>sync root to lock against</summary>
        private static readonly object SyncRoot = new Object();

        /// <summary>Start time</summary>
        public DateTime StartTime { get; set; }

        private TraceLogger _traceLogger;
        public TraceLogger TraceLogger
        {
            get { return _traceLogger ?? (_traceLogger = new TraceLogger()); }
        }

        /// <summary>
        /// Physical Hosting environment
        /// </summary>
        [DataMember]
        public IHostingEnvironmentPhysical HostEnvPhysical;

        /// <summary>
        /// Hosting environment
        /// </summary>
        [DataMember]
        public IHostingEnvironment HostEnv { get; set; }

        /// <summary>Break and exit the current processing, used for external debuggers etc</summary>
        public bool BreakExit { get; private set; }

        public bool ForceCleanExit { get; set; }

        /// <summary>
        /// Are we using real hardware
        /// </summary>
        public bool UsingHW { get; set; }

        /// <summary>
        /// Set restart on robotic hardware failure to restart
        /// </summary>
        public bool ServiceFailureRestart { get; private set; }

        /// <summary>
        /// private contsturctor
        /// </summary>
        private SimEnv()
        {
            StartTime = DateTime.Now;
            UsingHW = false;
        }

        /// <summary>
        /// get the simenv object
        /// </summary>
        public static SimEnv Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (SyncRoot)
                    {
                        if (_instance == null)
                            _instance = new SimEnv();
                    }
                }

                return _instance;
            }
        }


        public event Action OnMainLoopStartEvent;
        public void MainLoopStart()
        {

            if (OnMainLoopStartEvent != null)
                OnMainLoopStartEvent();
        }

        public event Action OnMainLoopEndEvent;
        public void MainLoopEnd()
        {
            if (OnMainLoopEndEvent != null)
                OnMainLoopEndEvent();
        }

        public event Action<IOrganism> OnSetSelectedOrganismEvent;
        public void SetSelectedOrganism(IOrganism organism)
        {
            if (OnSetSelectedOrganismEvent != null)
                OnSetSelectedOrganismEvent(organism);
        }

        public bool PauseState { get; private set; }
        public event Action<bool> OnPauseEvent;

        public void SetPause(bool pause)
        {
            PauseState = pause;
            if (OnPauseEvent != null)
                OnPauseEvent(PauseState);
        }
        public void TogglePause()
        {
            SetPause(PauseState ^= true);
        }

        public void ClearBreakFlag()
        {
            BreakExit = false;
        }

        public void ClearServiceFailureRestartFlag()
        {
            ServiceFailureRestart = false;
        }

        public void TriggerScenarioRestart(string reason)
        {
            TraceLogger.LogWarn("Restarting Scenario : " + reason);
            BreakExit = true;
            Globals.ContinousRestart = true;
        }

        public void TriggerApplicationExit()
        {
            TraceLogger.LogWarn("TriggerApplicationExit - Exiting");
            BreakExit = true;
            Globals.ContinousRestart = false;
        }

        public void TriggerServiceAndScenarioRestart(string reason)
        {
            TriggerScenarioRestart(reason);
            TriggerServiceRestart(reason);
        }

        public event Action ServiceExit;
        private void TriggerServiceRestart(string reason)
        {
            TraceLogger.LogWarn("Restarting Service : " + reason);

            if (ServiceExit != null)
                ServiceExit();

            ServiceFailureRestart = true;
        }

        /// <summary>
        /// Exception Logger
        /// </summary>
        /// <param name="e"></param>
        public void ExceptionLogger(Exception e)
        {
            TraceLogger.LogWarn("Caught : " + e.Message);
            TraceLogger.LogWarn("Stack : " + e.StackTrace);
            TraceLogger.LogWarn("Source :  " + e.Source);
            TraceLogger.LogWarn("Target Site : " + e.TargetSite);

            // check inner exception
            if (e.InnerException != null)
            {
                if (!(string.IsNullOrEmpty(e.InnerException.Message)))
                {
                    TraceLogger.LogWarn("Inner Exception Message : " + e.InnerException.Message);
                }

                if (!(string.IsNullOrEmpty(e.InnerException.StackTrace)))
                {
                    TraceLogger.LogWarn("Inner Exception Stack : " + e.InnerException.StackTrace);
                }
            }


            // some basic details to help track down the issue
            TraceLogger.LogWarn(string.Format("Memory Usage : {0}", Environment.WorkingSet));
            TraceLogger.LogWarn(string.Format("Processor count : {0}", Environment.ProcessorCount));
            TraceLogger.LogWarn(string.Format("OS Version : {0}", Environment.OSVersion));
            if (HostEnv != null)
                TraceLogger.LogWarn(string.Format("Hosting Environment {0} version {1}", HostEnv.GetType(),
                                                  HostEnv.VersionInformation));


            // upload the logs
            TraceLogger.UploadLogs("Error");

#if DEBUG
            if (Globals.LaunchedFrom == LaunchType.CommandLine)
            {
                // todo : temp on while debugging
                TraceLogger.LogWarn("Press enter to continue");
                Console.ReadLine();
            }
#endif
        }

        /// <summary>
        /// Exception catcher across threads 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void ExceptionHandler(Object sender, UnhandledExceptionEventArgs args)
        {
            ExceptionLogger((Exception)args.ExceptionObject);
        }
    }
}